package com.openx.cloud.server.autoconfigure.swagger;

import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.service.ApiInfo;

import java.util.*;

/**
 * 接口扫描
 *
 * @author LYD
 */
@Slf4j
public class RequestMappingScan implements ApplicationListener<ApplicationReadyEvent> {
    private AmqpTemplate amqpTemplate;
    private ApiInfo apiInfo;
    private static final AntPathMatcher pathMatch = new AntPathMatcher();
    private SwaggerProperties swaggerProperties;

    public RequestMappingScan(AmqpTemplate amqpTemplate, SwaggerProperties swaggerProperties, ApiInfo apiInfo) {
        this.amqpTemplate = amqpTemplate;
        this.swaggerProperties = swaggerProperties;
        this.apiInfo = apiInfo;
    }

    /**
     * 初始化方法
     *
     * @param event
     */
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        try {
            ConfigurableApplicationContext applicationContext = event.getApplicationContext();
            amqpTemplate = applicationContext.getBean(RabbitTemplate.class);
            if (amqpTemplate == null || swaggerProperties == null || !swaggerProperties.isRegister()) {
                return;
            }
            Environment environment = applicationContext.getEnvironment();

            // 服务名称
            String serviceId = environment.getProperty("spring.application.name", "application");
            // 当前环境
            String env = environment.getProperty("spring.profiles.active");
            // 所有接口映射
            RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
            // 获取url与类和方法的对应信息
            Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
            List<Map<String, String>> list = new ArrayList<Map<String, String>>();

            for (Map.Entry<RequestMappingInfo, HandlerMethod> m : map.entrySet()) {
                RequestMappingInfo requestMappingInfo = m.getKey();
                HandlerMethod method = m.getValue();
                if (method.getMethodAnnotation(ApiIgnore.class) != null) {
                    // 忽略的接口不扫描
                    continue;
                }
                if (method.getMethod().getDeclaringClass().getAnnotation(RestController.class) == null) {
                    // 只扫描RestController
                    continue;
                }
                Set<MediaType> mediaTypeSet = requestMappingInfo.getProducesCondition().getProducibleMediaTypes();
                for (MethodParameter params : method.getMethodParameters()) {
                    if (params.hasParameterAnnotation(RequestBody.class)) {
                        mediaTypeSet.add(MediaType.APPLICATION_JSON);
                        break;
                    }
                }
                String mediaTypes = getMediaTypes(mediaTypeSet);
                // 请求类型
                RequestMethodsRequestCondition methodsCondition = requestMappingInfo.getMethodsCondition();
                String methods = getMethods(methodsCondition.getMethods());
                // 请求路径
                PatternsRequestCondition p = requestMappingInfo.getPatternsCondition();
                String paths = getUrls(p.getPatterns());
                // 类名
                String className = method.getMethod().getDeclaringClass().getName();
                // 方法名
                String methodName = method.getMethod().getName();
                String fullName = className + "." + methodName;
                // md5码
                String name = "";
                String description = "";
                String code = "api@" + fullName;

                ApiOperation apiOperation = method.getMethodAnnotation(ApiOperation.class);
                if (apiOperation != null) {
                    name = apiOperation.value();
                    description = apiOperation.notes();
                }
                name = StringUtils.isBlank(name) ? methodName : name;

                Map<String, String> info = new HashMap<>();
                info.put("name", name);
                info.put("code", code);
                info.put("description", description);
                info.put("path", paths);
                info.put("className", className);
                info.put("methodName", methodName);
                info.put("method", methods);
                info.put("serviceId", serviceId);
                info.put("contentType", mediaTypes);
                list.add(info);
            }
            Map result = new HashMap(2);
            result.put("serviceId", serviceId);
            result.put("apis", list);
            result.put("title", apiInfo.getTitle());
            result.put("description", apiInfo.getDescription());
            result.put("license", apiInfo.getLicense());
            result.put("licenseUrl", apiInfo.getLicenseUrl());
            result.put("version", apiInfo.getVersion());
            log.info("发送Swagger注册消息:{}", JSONObject.toJSONString(result));
            // 发送mq扫描消息
            amqpTemplate.convertAndSend(swaggerProperties.getRegisterRouteKey(), result);
        } catch (Exception e) {
            log.error("注册Swagger接口发送失败:{}", e);
        }
    }


    private String getMediaTypes(Set<MediaType> mediaTypes) {
        StringBuilder sbf = new StringBuilder();
        for (MediaType mediaType : mediaTypes) {
            sbf.append(mediaType.toString()).append(",");
        }
        if (mediaTypes.size() > 0) {
            sbf.deleteCharAt(sbf.length() - 1);
        }
        return sbf.toString();
    }

    private String getMethods(Set<RequestMethod> requestMethods) {
        StringBuilder sbf = new StringBuilder();
        for (RequestMethod requestMethod : requestMethods) {
            sbf.append(requestMethod.toString()).append(",");
        }
        if (requestMethods.size() > 0) {
            sbf.deleteCharAt(sbf.length() - 1);
        }
        return sbf.toString();
    }

    private String getUrls(Set<String> urls) {
        StringBuilder sbf = new StringBuilder();
        for (String url : urls) {
            sbf.append(url).append(",");
        }
        if (urls.size() > 0) {
            sbf.deleteCharAt(sbf.length() - 1);
        }
        return sbf.toString();
    }


}
